#include "PlatoonAttackState.h"

PlatoonAttackState::PlatoonAttackState(Context* context) : State(context), mTarget(0)
{
	Platoon* platoon = static_cast<Platoon*>(mContext);
	
	mAttackingGroup = UnitGroup(platoon->getUnits());
}

PlatoonAttackState::~PlatoonAttackState(void)
{
}

void PlatoonAttackState::handle()
{
	Platoon* platoon = static_cast<Platoon*>(mContext);

	// If the enemy was  killed or escaped
	if(EnemyManager::self() && !EnemyManager::self()->hasVisibleUnits())
	{
		platoon->changeState(new PlatoonScoutState(mContext));
	}


	else if(!mTarget || mTarget->getHitPoints() == 0)
	{
		refreshEnemyTargetTable();
		updateAttackingGroup();
		updateDancingGroup();

		mTarget = platoon->getBestTarget(EnemyManager::self()); //FIXME deveria ser o BestTarget do mAttackingGroup
		//platoon->attack(mTarget);
		mAttackingGroup.attackUnit(mTarget);
	}
}

void PlatoonAttackState::updateAttackingGroup()
{
	std::set<BWAPI::Unit*>::iterator it;
	for(it = mAttackingGroup.begin(); it != mAttackingGroup.end(); ++it)
	{
		BWAPI::Unit* unit = *it;

		BWAPI::Unit* attacker = unitIsBeenAttacked(unit);
		if((unit->getHitPoints() < (int)(HpThreshold() * unit->getInitialHitPoints())) 
					&& attacker)
		{
			double distance = unit->getDistance(attacker);
			BWAPI::Position unitPos = unit->getPosition();
			BWAPI::Position attPos = attacker->getPosition();
			
			BWAPI::Position A((int) ((double)(unitPos.y() - attPos.y()) / distance), 
						(int) ((double)(unitPos.x() - attPos.x()) / distance));

			BWAPI::Position safePosition(unitPos.x() + A.x() * (SAFE_DIST + Platoon::DRAGOON_RANGE - distance), 
						unitPos.y() + A.y() * (SAFE_DIST + Platoon::DRAGOON_RANGE - distance));
			
			unit->move(safePosition);
			mAttackingGroup.erase(std::remove(mAttackingGroup.begin(), mAttackingGroup.end(), unit), mAttackingGroup.end());
			mDancingGroup.insert(unit);
		}
	}
}

void PlatoonAttackState::updateDancingGroup()
{
	std::set<BWAPI::Unit*>::iterator it;
	for(it = mDancingGroup.begin(); it != mDancingGroup.end(); ++it)
	{
		BWAPI::Unit* unit = *it;

		if(!unitIsBeenAttacked(unit))
		{
			mDancingGroup.erase(std::remove(mDancingGroup.begin(), mDancingGroup.end(), unit), mDancingGroup.end());
			mAttackingGroup.insert(unit);
		}

		else
		{
			// TODO Fugir mais ainda?
		}
	}
}

void PlatoonAttackState::refreshEnemyTargetTable()
{
	mEnemyTargetTable.clear();
	
	UnitGroup& enemies = EnemyManager::self()->getVisibleUnits();
	std::set<BWAPI::Unit*>::iterator it;

	for(it = enemies.begin(); it != enemies.end(); ++it)
	{
		BWAPI::Unit* unit = *it;
		
		BWAPI::Unit* target = unit->getTarget();
		if(!target)
			target = unit->getOrderTarget();
		
		if(target)
			mEnemyTargetTable.insert(std::pair<BWAPI::Unit*, BWAPI::Unit*>(target, unit));
	}
}

BWAPI::Unit* PlatoonAttackState::unitIsBeenAttacked(BWAPI::Unit* unit)
{
	std::pair<std::multimap<BWAPI::Unit*, BWAPI::Unit*>::iterator, 
				std::multimap<BWAPI::Unit*, BWAPI::Unit*>::iterator > its;
	its = mEnemyTargetTable.equal_range(unit);

	if(its.first == mEnemyTargetTable.end())
		return 0;
	
	else
	{
		std::multimap<BWAPI::Unit*, BWAPI::Unit*>::iterator it = its.first;
		BWAPI::Unit* result = (*it).second;
		it++;
		int distance = (int)result->getDistance(unit);

		for( ; it != its.second; ++it)
		{
			BWAPI::Unit* tmp = (*it).second;
			int tmpDistance = (int)tmp->getDistance(unit);

			if(tmpDistance < distance)
			{
				result = tmp;
				distance = tmpDistance;
			}
		}

		return result;
	}
}

/*bool PlatoonAttackState::unitIsBeenAttacked(BWAPI::Unit* unit)
{
	std::pair<std::multimap<BWAPI::Unit*, BWAPI::Unit*>::iterator, 
				std::multimap<BWAPI::Unit*, BWAPI::Unit*>::iterator > its;
	its = mEnemyTargetTable.equal_range(unit);

	if(its.first == mEnemyTargetTable.end())
		return false;
	else
		return true;
}*/
